#lang racket

(require racket/set)
(require parser-tools/lex)
(require (prefix-in : parser-tools/lex-sre))

(define parens (make-parameter empty))
(define exit-cont (make-parameter (λ ()
                                    (printf "(ENDMARKER)~n")
                                    (exit))))

(define open-parens (set "(" "[" "{"))
(define close-parens (set ")" "]" "}"))

(define-lex-abbrev keyword (:or "new" "goto" "if" "len"))
(define-lex-abbrev type (:or "int"))
(define-lex-abbrev punct (:or "?" ":" "[" "]" "+" "-" "(" ")" ";" "{" "}"
                              "<" ">" "==" "<=" ">=" "!=" "=" ","))

(define-lex-abbrev integer (:+ numeric))
;$[_a-zA-Z][_a-zA-Z0-9]*
(define-lex-abbrev identifier (:: (:or "_" alphabetic)
                                  (:* (:or "_" alphabetic numeric))))

(define (matches paren)
  (cond
    [(empty? (parens)) (error 'parens "~e has no opening pair" paren)]
    [(and (equal? ")" paren) (equal? "(" (first (parens)))) #t]
    [(and (equal? "]" paren) (equal? "[" (first (parens)))) #t]
    [(and (equal? "}" paren) (equal? "{" (first (parens)))) #t]
    [else #f]))

(define (c-lex-normal)
  ((lexer
    [integer (begin
               (printf "(LIT ~a)~n" lexeme)
               (c-lex-normal))]
    [punct (parameterize ([parens
                           (cond
                             [(set-member? open-parens lexeme) (cons lexeme (parens))]
                             [(set-member? close-parens lexeme)
                              (if (matches lexeme)
                                  (rest (parens))
                                  (error 'parens "mismatched closing parenthesis: ~e, ~e" lexeme (parens)))]
                             [else (parens)])])
             (printf "(PUNCT \"~a\")~n" lexeme)
             (c-lex-normal))]
    [type (begin
            (printf "(TYPE \"~a\")~n" lexeme)
            (c-lex-normal))]
    [keyword (begin
               (printf "(KEYWORD \"~a\")~n" lexeme)
               (c-lex-normal))]
    [identifier (begin
                  (printf "(ID \"~a\")~n" lexeme)
                  (c-lex-normal))]
    #;[(:: (:* (:or " " "\t"))
         (:+ (:or "\n" "\r"))
         (:* whitespace))
     (begin
       #;(when (empty? (parens))
         (printf "(NEWLINE)~n"))
       (c-lex-normal))]
    #;[(:+ (:or " " "\t")) (c-lex-normal)]
    [whitespace (c-lex-normal)]
    [(eof) ((exit-cont))]
    [any-char (error 'lexer "unrecognized input: ~e" lexeme)])
   (current-input-port)))

(port-count-lines! (current-input-port))
(c-lex-normal)